package com.atguigu.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.HttpRequestHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Map;

/**
 * SpringMVC除了在方法上传入原生的request、session、response；还能怎样将数据带回给页面
 *
 *      1）、将数据存储在request请求域中
 *          可以在方法处传入Map 或 Model 或 ModelMap（三种任选其一）。给这些参数里面保存的所有数据都会放在域中。可以在页面中获取
 *          （1）Map、Model、ModelMap存储的数据，全都在【request】请求域中
 *          （2）Map、Model、ModelMap的【类型都是class org.springframework.validation.support.BindingAwareModelMap】
 *              相当于三个入参的参数，是一个；并且存储的数据BindingAwareModelMap会放在请求域中
 *
 *              Map、Model、ModelMap的关系
 *               Map（Interface）★★★
 *                      ||
 *               LinkedHashMap（Class）
 *                      ||
 *               ModelMap（Class）     Model（Interface）★★★
 *                       \\              //
 *                        \\            //
 *                          ExtendedModelMap
 *                                  ||
 *                                  \/
 *                          BindingAwareModelMap★★★
 *
 *      2）、方法的返回值可以变为ModelAndView类型（页面和数据的合体类型）
 *              既包含视图信息（页面地址），也包含模型数据（给页面返回的数据）
 *              而且数据保存在request请求域中
 *
 * ★★★为什么放在request域中？
 *     首先我们能够使用pageContext、request域、session域、application域
 *
 *     page：域只能在页面使用
 *     request域：服务器收到请求，处理完成之后返回信息给客户端，之后，这个请求就消失了，服务器内存就释放了
 *     session域：数据存储在服务器上，时间一长服务器就挂了
 *     application域：域中的数据不仅你能看到，别人也能看到
 *
 *     基于以上考虑，SpringMVC将采用request域存储数据
 *
 *
 *      3）、SpringMVC提供了一种，可以临时给Session域中保存数据的方式
 *           使用一个注解@SessionAttributes，需要和BindingAwareModelMap 或 ModelAndView配合使用
 *
 *           适用范围：标注在类上
 *           属性：
 *              value = {"key"}：
 *                  给BindingAwareModelMap 或 ModelAndView中，保存的数据，就同时在Session域中保存一份。
 *                  value的值：保存数据时要给Session中存放的数据的key
 *
 *              type ={String.class}
 *                  只要BindingAwareModelMap 或 ModelAndView中，保存的是这种类型的数据，都在Session域中存放一份。不管数据存放时，使用什么key值
 *                  value 和 type一起使用，type优先级更高
 *
 *           注意：
 *              value可以精确控制存储的数据，type会将类型的全部存储容易session爆炸
 *
 *          【推荐】：使用原生API，给session中存储数据
 *                  @SessionAttributes就不要使用了！！！
 *                  可能会引发异常，
 *
 *                  如果一定要使用@SessionAttributes
 *                      1）、保证隐含模型中，一定有@SessionAttributes标注的属性。
 *                      2）、隐含模型中，没有指定属性，session中也没有，就会抛异常！
 *
 * @author 伟哥
 * @date 2021-05-09 12:53
 */
@SessionAttributes(value = {"msg","haha"},types = {String.class})
@Controller
public class OutputController {
    /**
     * Map 是一个【接口】
     * map存储的数据，在request域中
     * @param map
     * @return
     */
    @RequestMapping(value = "/handle01")
    public String handle01(Map<String,Object> map) {
        System.out.println("handle01...");
        /*
            1、在方法的入参处，填写map
            2、使用map，向里面填入数据
         */
        map.put("msg","你好！");
        System.out.println("map的类型："+map.getClass());
        //3、在页面中获取src/main/webapp/WEB-INF/pages/success.jsp

        map.put("haha","哈哈");

        return "success";
    }

    /**
     * Model是一个【接口】
     * @return
     */
    @RequestMapping(value = "/handle02")
    public String handle02(Model model){
        System.out.println("handle02...");
        /*
            1、在方法的入参处，填写Model【没错就是，直接写接口，SpringMVC会自动实现该接口】
            2、使用model，向里面填入数据：可以传递key、val，也可以传递一个map
         */
        model.addAttribute("msg","你好坏！");
        System.out.println("model的类型："+model.getClass());
        //3、在页面中获取src/main/webapp/WEB-INF/pages/success.jsp

        model.addAttribute("haha",18);

        return "success";
    }

    /**
     * ModelMap是一个类
     * @param modelMap
     * @return
     */
    @RequestMapping(value = "/handle03")
    public String handle03(ModelMap modelMap){
        System.out.println("handle03...");
        /*
            1、在方法的入参处，填写modelMap【没错就是，直接写接口，SpringMVC会自动实现该接口】
            2、使用modelMap，向里面填入数据：可以传递key、val，也可以传递一个map
         */
        modelMap.addAttribute("msg","你真好！");
        System.out.println("modelMap的类型："+modelMap.getClass());
        //3、在页面中获取src/main/webapp/WEB-INF/pages/success.jsp

        return "success";
    }

    /**
     * ModelAndView
     * @param
     * @return
     */
    @RequestMapping(value = "/handle04")
    public ModelAndView handle04(){
        /*
            ModelAndView是一个类，所以可以直接new
            有参构造器：new的时候，可以传递视图名，这个视图名就是，页面的名字。最终，视图解析器会帮助我们拼接字符串，得到真实地址
            无参构造器：先new，在设置，视图名（返回地址）
                    ModelAndView modelAndView = new ModelAndView();
                    modelAndView.setViewName("success");
         */

        ModelAndView mv = new ModelAndView("success");
        // 存储数据
        mv.addObject("msg","你好吗？");

        return mv;
    }
}
